﻿using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using LucyGrid;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using System.IO;
using System.Collections.Concurrent;

namespace LucyGridTest
{
	/// <summary>
	/// Summary description for GridEnpoint
	/// </summary>
	[TestClass]
	public class GridEnpoint
	{
		public GridEnpoint()
		{
			//
			// TODO: Add constructor logic here
			//
		}

		private TestContext testContextInstance;

		/// <summary>
		///Gets or sets the test context which provides
		///information about and functionality for the current test run.
		///</summary>
		public TestContext TestContext
		{
			get
			{
				return testContextInstance;
			}
			set
			{
				testContextInstance = value;
			}
		}

		#region Additional test attributes
		//
		// You can use the following additional attributes as you write your tests:
		//
		// Use ClassInitialize to run code before running the first test in the class
		// [ClassInitialize()]
		// public static void MyClassInitialize(TestContext testContext) { }
		//
		// Use ClassCleanup to run code after all tests in a class have run
		// [ClassCleanup()]
		// public static void MyClassCleanup() { }
		//
		// Use TestInitialize to run code before running each test 
		// [TestInitialize()]
		// public void MyTestInitialize() { }
		//
		// Use TestCleanup to run code after each test has run
		// [TestCleanup()]
		// public void MyTestCleanup() { }
		//
		#endregion

		[TestMethod]
		public void GridEndpoint_StressTest()
		{
			GridUtils.EnableLog = -1;
			var endpoint = string.Format("http://{0}:{1}/", Environment.MachineName, 9099);
			var server = new GridEndpoint(endpoint);
			var ev = new ManualResetEvent(false);
			server.RequestReceived += (sender, context) =>
			{
				if (context.Request.Url.ToString().Contains("stop"))
				{
					ev.Set();
					return;
				}
				// Try to accumulate request
				Thread.Sleep(500);
				context.Response.StatusCode = 200;
				using (var sw = new StreamWriter(context.Response.OutputStream))
					sw.Write(DateTime.Now.ToString("hh:mm:ss"));
			};
			server.Start();
			var error = false;
			ThreadPool.SetMinThreads(100, 100);
			Task.Run(() =>
			{
				var tasks = Enumerable.Range(1, 1000)
					//.AsParallel()
					//.WithDegreeOfParallelism(8)
					.Select(i =>
					{
						var wc = new System.Net.WebClient();
						return wc.DownloadStringTaskAsync(endpoint);
					})
					.ToArray();
				Task.WaitAll(tasks);
				var res = tasks.Select(t => t.Result).ToArray();
				error = res.Length != tasks.Length;
				res.GroupBy(r => r)
					.All(g =>{
						System.Diagnostics.Debug.WriteLine(string.Format("{0}, {1}", g.Key, g.Count()));
							return true;
					});
				ev.Set();
			});
			ev.WaitOne();
			Assert.IsFalse(error);
			// Wait for log
			Thread.Sleep(2000);
		}

		public class WebAppTest
		{
			public string WebAppTestOperation()
			{
				return GridMe("Hello world " + DateTime.Now).ToString();
			}

			GridObject GridMe(string data)
			{
				GridObject o = new GridObject()
				{
					id = (int)DateTime.Now.TimeOfDay.TotalMilliseconds,
					data = "Hello world ",
					processStart = DateTime.Now
				};
				inq.Enqueue(o);
				while (true)
				{
					gridEvent.WaitOne(1000);
					if (outq.ContainsKey(o.id))
					{
						if (outq.TryRemove(o.id, out o))
							return o;
					}
				}
			}


			[Serializable]
			class GridObject
			{
				public int id;
				public string data;
				public DateTime processStart;
				public DateTime processGrid;
				public DateTime processEnd;

				public override string ToString()
				{
					return string.Format("S: {0} G:{1} E:{2} DATA:{3}", processStart.ToString("hh:mm:ss.fff")
						, processGrid.ToString("hh:mm:ss.fff")
						, processEnd.ToString("hh:mm:ss.fff")
						, data);
				}
			}
			static ConcurrentQueue<GridObject> inq;
			static ConcurrentDictionary<int, GridObject> outq;
			static AutoResetEvent gridEvent;
			static WebAppTest()
			{
				gridEvent = new AutoResetEvent(false);
				GridUtils.EnableLog = -1;
				inq = new ConcurrentQueue<GridObject>();
				outq = new ConcurrentDictionary<int, GridObject>();
				Task.Run(() => Worker());
			}
			static IEnumerable<GridObject> Generator()
			{
				GridObject outo;
				while (true)
				{
					if (inq.TryDequeue(out outo))
						yield return outo;
					else
						Thread.Sleep(200);
				}
			}
			static void Worker()
			{
				foreach (var o in Generator().AsParallelGrid(o => { o.processGrid = DateTime.Now; return o; }, new GridSettings() { EnableLocalProcessing = true }))
				{
					o.processEnd = DateTime.Now;
					outq.TryAdd(o.id, o);
					gridEvent.Set();
				}
			}
		}

		[TestMethod]
		public void GridEndpoint_TestWebService()
		{
			GridUtils.EnableLog = -1;
			var service = new WebAppTest();
			var endpoint = string.Format("http://{0}:{1}/", Environment.MachineName, 9099);
			var server = new GridEndpoint(endpoint);
			var ev = new ManualResetEvent(false);
			server.RequestReceived += (sender, context) =>
			{
				if (context.Request.Url.ToString().Contains("stop"))
				{
					ev.Set();
					return;
				}
				context.Response.StatusCode = 200;
				using (var sw = new StreamWriter(context.Response.OutputStream))
					sw.Write(service.WebAppTestOperation());
			};
			server.Start();
			ev.WaitOne();
		}
	}
}
